/*****************************************************************************/
/*!	\file		PIS10CreateServerClient.c
 *	\brief 		C Source code file for the PIS10 Stack Example
	\par        Dalian Yunxing Tech Co., Ltd.

				Dalian, China
				Phone   : +86 (411) 8825 4852
				Email   : yx@yunxing.tech
 */
/*****************************************************************************/
/******************************************************************************
*	Includes
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "PIS10CreateServerClient.h"

/******************************************************************************
*	Variables
******************************************************************************/
IEC61850 myServerClient = NULL;

/******************************************************************************
*	Functions
******************************************************************************/

/******************************************************************************
*       CreateServer Function Definition
******************************************************************************/
/*!  \brief         Creates a IEC61850 Server Object, using the default parameters
 *
 *   \ingroup      IEC61850
 *
 *   \param[in]      uiOptions		An unsigned long int that holds the create parameter options
 *
 *   \return       enum IEC61850_ErrorCodes, IEC61850_ERROR_NONE  on Success
 *	 \return		otherwise corresponding error code returned from IEC61850 IEC61850_Create function
 ******************************************************************************/
enum IEC61850_ErrorCodes CreateServer(unsigned long int uiOptions)
{

	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE ;
	struct IEC61850_Parameters tServerParam = {0};

	memset(&tServerParam, 0, sizeof(struct IEC61850_Parameters) ); //Always memset IEC61850_Parameters struct to 0 to avoid garbage values

	if(IsMyServerClientNULL() == 1) //If the server does not already exist
	{
		tServerParam.ClientServerFlag    = IEC61850_SERVER;		// This is a IEC 61850 Server
		tServerParam.Ed1_Ed2_Flag		 = IEC61850_Edition2;	// Set PIS10 Stack to use Edition2
		tServerParam.uiOptions           = uiOptions;			// Set Options Flags
		tServerParam.ptReadCallback      = ReadCallbackHandler;				// Set Read Callback Function
		tServerParam.ptWriteCallback     = WriteCallbackHandler;			// Set Write Callback Function
		tServerParam.ptUpdateCallback    = UpdateCallbackHandler;			// Set Update Callback Function
		tServerParam.ptSelectCallback    = SelectCallbackHandler;	// Set Select Callback Function
		tServerParam.ptOperateCallback   = OperateCallbackHandler;// Set Operate Callback Function
		tServerParam.ptCancelCallback    = CancelCallbackHandler;	// Set Cancel Callback Function
		tServerParam.ptErrorCallback	 = ErrorCallbackHandler;			// Set Error Callback Function
		tServerParam.ptOprTestCallback	 = OperativeTestCallbackHandler;	//Set Operative test Callback Function
		tServerParam.ptQuestionableCallback = QuestionableCallbackHandler; //Set Questionable Data Point Callback Function
		tServerParam.ptCmdTermCallback	= CommandTerminationCallback;	//Set Command Termination Callback Function
        tServerParam.ptFileCallback = FileCallbackHandler;  //Set File handler callback 

		myServerClient = IEC61850_Create(&tServerParam, &eErrorCode);

	    if(myServerClient == NULL)
	    {
	    	printf("   Server Failed to create: %s (%i)\n", IEC61850_ErrorString(eErrorCode), eErrorCode);

			if ((eErrorCode == IEC61850_ERROR_GOOSE_SERVICE_FAILED) || (eErrorCode == IEC61850_ERROR_GOOSE_INIT_FAILED))
			{
				printf("NOTE: GOOSE Protocol is not supported by WIFI networks, please make sure you are using a Wired Ethernet Adapter\n Make sure your user has permission to run raw socket capture (pcap)\n\n");
			}
	    }
	}
	else
	{
		eErrorCode = IEC61850_ERROR_INVALID_STATE;
	}

	return eErrorCode;

}

/******************************************************************************
*       CreateClient Function Definition
******************************************************************************/
/*!  \brief        Creates a IEC61850 Client Object, using the default paramaters
 *
 *   \ingroup      IEC61850
 *
 *   \return       enum IEC61850_ErrorCodes, IEC61850_ERROR_NONE  on Success
 *	 \return	   otherwise corresponding error code returned from IEC61850 IEC61850_Create function
 ******************************************************************************/
enum IEC61850_ErrorCodes CreateClient(void)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE ;
	struct IEC61850_Parameters tClientParam = {0};
	unsigned long int uiOptions = 0;

	memset(&tClientParam, 0, sizeof(struct IEC61850_Parameters) );//Always memset IEC61850_Parameters struct to 0 to avoid garbage values

	if(IsMyServerClientNULL() == 1) //If the Client does not already exist
	{
		tClientParam.ClientServerFlag    = IEC61850_CLIENT;		// This is a IEC 61850 Client
		tClientParam.Ed1_Ed2_Flag		 = IEC61850_Edition2;	// Set PIS10 Stack to use Edition2
		tClientParam.uiOptions           = uiOptions;			// Set Options Flags
	//	tClientParam.ptReadCallback      = ReadCallbackHandler;				// Set Read Callback Function
	//	tClientParam.ptWriteCallback     = WriteCallbackHandler;			// Set Write Callback Function
	 	tClientParam.ptUpdateCallback    = UpdateCallbackHandler;			// Set Update Callback Function
	//	tClientParam.ptSelectCallback    = SelectCallbackHandler;	// Set Select Callback Function
	//	tClientParam.ptOperateCallback   = OperateCallbackHandler;// Set Operate Callback Function
	//	tClientParam.ptCancelCallback    = CancelCallbackHandler;	// Set Cancel Callback Function
		tClientParam.ptErrorCallback	 = ErrorCallbackHandler;			// Set Error Callback Function
	//	tClientParam.ptOprTestCallback	 = OperativeTestCallbackHandler;	//Set Operative test Callback Function
		tClientParam.ptQuestionableCallback = QuestionableCallbackHandler;	//Set Questionable Data Point Callback Function
		tClientParam.ptCmdTermCallback	 = CommandTerminationCallback;	//Set Command Termination Callback Function
		tClientParam.DataMapMode = IEC61850_MAP_MODE_DAID;
        
		myServerClient = IEC61850_Create(&tClientParam, &eErrorCode);

		if(myServerClient == NULL)
		{
			printf("   Client Failed to create: %s (%i)\n", IEC61850_ErrorString(eErrorCode), eErrorCode);
			 if ((eErrorCode == IEC61850_ERROR_GOOSE_SERVICE_FAILED) || (eErrorCode == IEC61850_ERROR_GOOSE_INIT_FAILED))
			{
				printf("NOTE: GOOSE Protocol is not supported by WIFI networks, please make sure you are using a Wired Ethernet Adapter\n Make sure your user has permission to run raw socket capture (pcap)\n\n");
			}
		}
	}
	else
	{
		eErrorCode = IEC61850_ERROR_INVALID_STATE;
	}

	return eErrorCode;
}


/******************************************************************************
*       LoadSCLFile Function Definition
******************************************************************************/
/*!  \brief          Loads and SCL file into the server given a path
 *
 *   \ingroup        IEC61850
 *
 *   \param[in]      pPath		A char * whcih holds the path and name of the SCL file
 *
 *   \return         enum IEC61850_ErrorCodes, IEC61850_ERROR_NONE  on Success
 *	 \return		 otherwise corresponding error code returned from IEC61850_LoadSCLFile function
 ******************************************************************************/
enum IEC61850_ErrorCodes LoadSCLFile(char *pFileName)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;
	IEC61850 myIEC61850Object = GetMyServerClient();

	eErrorCode = IEC61850_LoadSCLFile(myIEC61850Object, pFileName); // Load in IED configuration SCL file

	if(eErrorCode != IEC61850_ERROR_NONE)
	{
		printf("   Loading SCL file (%s) has failed: %s (%i)\n", pFileName, IEC61850_ErrorString(eErrorCode), eErrorCode);

		if ((eErrorCode == IEC61850_ERROR_INVALID_IP_ADDRESS)||(eErrorCode == IEC61850_ERROR_GOOSE_SERVICE_FAILED) || (eErrorCode == IEC61850_ERROR_GOOSE_INIT_FAILED))
		{
			printf("\nPlease check that you have set your IP address in the CID file %s \n", pFileName);

			printf("<Address>\n   <P type = \"OSI-AP-Title\">1, 1, 9999, 1</P>\n   <P type = \"OSI-AE-Qualifier\">12</P>\n   <P type = \"OSI-PSEL\">00000001</P>\n   <P type = \"OSI-SSEL\">0001</P>\n   <P type = \"OSI-TSEL\">0001</P>\n");
			printf("   <P type = \"IP\">192.168.1.100</P>   <!--Put the IP Address Here-->\n   <P type = \"IP-SUBNET\">255.255.255.0</P>\n   <P type = \"IP-GATEWAY\">192.168.1.1</P>\n</Address>\n");

			printf("NOTE: GOOSE Protocol is not supported by WIFI networks, please make sure you are using a Wired Ethernet Adapter\n Make sure your user has permission to run raw socket capture (pcap)\n\n");
		}
	}

	return eErrorCode;
}

/******************************************************************************
*       StartMyServerClient Function Definition
******************************************************************************/
/*!  \brief          Start IEC61850 object communications.
 *
 *   \ingroup        IEC61850
 *
 *   \return         enum IEC61850_ErrorCodes, IEC61850_ERROR_NONE  on Success
 *	 \return		 otherwise corresponding error code returned from IEC61850_Start function
 ******************************************************************************/
enum IEC61850_ErrorCodes StartMyServerClient(void)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;
	IEC61850 myIEC61850Object = GetMyServerClient();

	eErrorCode = IEC61850_Start(myIEC61850Object);

	if(eErrorCode != IEC61850_ERROR_NONE)
	{
		printf("   Can't start server: %s (%i)\n", IEC61850_ErrorString(eErrorCode), eErrorCode);

		if ((eErrorCode == IEC61850_ERROR_GOOSE_SERVICE_FAILED) || (eErrorCode == IEC61850_ERROR_GOOSE_INIT_FAILED))
		{
			printf("\nPlease check that you have set the correct IP address in the CID file\n");
			printf("NOTE: GOOSE Protocol is not supported by WIFI networks, please make sure you are using a Wired Ethernet Adapter\n Make sure your user has permission to run raw socket capture (pcap)\n\n");
		}
	}

	return eErrorCode;
}

/******************************************************************************
*       StopMyServerClient Function Definition
******************************************************************************/
/*!    \brief          Stops IEC61850 object communications.
 *
 *     \ingroup        IEC61850
 *
 *     \return         enum IEC61850_ErrorCodes, IEC61850_ERROR_NONE  on Success
 *	   \return		   otherwise corresponding error code returned from IEC61850_Stop function
 ******************************************************************************/
enum IEC61850_ErrorCodes StopMyServerClient(void)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;
	IEC61850 myIEC61850Object = GetMyServerClient();

	eErrorCode = IEC61850_Stop(myIEC61850Object);

	if((eErrorCode != IEC61850_ERROR_NONE) && (eErrorCode != IEC61850_ERROR_INVALID_STATE))
	{
		printf("   Can't stop server: %s (%i)\n", IEC61850_ErrorString(eErrorCode), eErrorCode);
	}

	return eErrorCode;


}


/******************************************************************************
*       IsMyServerClientNULL Function Definition
******************************************************************************/
/*!  \brief        checks if the ClientServer struct is not null
 *
 *   \ingroup      IEC61850
 *
 *   \return       returns 0 if the ClientServer struct is NOT null
 *	 \return	   returns 1 if the ClientServer struct is null
 ******************************************************************************/
int IsMyServerClientNULL(void)
{
	int isNULL = 1;

	if(GetMyServerClient() != NULL)
	{
		isNULL = 0;
	}

	return isNULL;
}

/******************************************************************************
*       GetMyServerClinet Function Definition
******************************************************************************/
/*!  \brief          gets a pointer to the MyServerClinet IEC61850 object
 *
 *    \ingroup       IEC61850
 *
 *    \return        a pointer an  IEC61850 object,
 *	  \return		 returns NULL if no object exists
 ******************************************************************************/
IEC61850 GetMyServerClient(void)
{
	return myServerClient;
}

/******************************************************************************
*       FreeMyServerClient Function Definition
******************************************************************************/
/*!  \brief          Free memory used by IEC 61850 object by calling IEC61850_Free()
 *
 *   \ingroup        IEC61850
 *
 *   \return         IEC61850_ERROR_NONE on success
 *   \return    	 otherwise error code
 ******************************************************************************/
enum IEC61850_ErrorCodes FreeMyServerClient(void)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;

    eErrorCode = IEC61850_Free(GetMyServerClient());  //Frees myServerClient

	if(eErrorCode == IEC61850_ERROR_NONE)
	{
	    myServerClient = NULL;
	}
	else
	{
		printf("   Free IEC61850 Object has failed: %s (%i)\n", IEC61850_ErrorString(eErrorCode), eErrorCode);
	}

	return eErrorCode;
}


/******************************************************************************
*       SetUserData Function Definition
******************************************************************************/
/*!  \brief          Sets the user data value for callbacks
 *
 *   \ingroup        IEC61850
 *
 *   \return         IEC61850_ERROR_NONE on success
 *   \return    	 otherwise error code
 ******************************************************************************/
enum IEC61850_ErrorCodes SetUserData(void * inData)
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;

	eErrorCode = IEC61850_SetUserData(GetMyServerClient(), inData);  //Sets the data pointer

	return eErrorCode;
}


/******************************************************************************
*       SetControlsOrCat Function Definition
******************************************************************************/
/*!  \brief          Sets the user data value for callbacks
 *
 *   \ingroup        IEC61850
 *
 *   \return         IEC61850_ERROR_NONE on success
 *   \return    	 otherwise error code
 ******************************************************************************/
enum IEC61850_ErrorCodes SetControlsOrCat()
{
	enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;
	enum eOriginatorCat OrCatValue;

	OrCatValue = ORCAT_BAY_CONTROL;

	//Call the API to set the OrCat Value for all controls, leace the OrIdent as the default - the IP address of client
	eErrorCode = IEC61850_SetOriginator(GetMyServerClient(), NULL, OrCatValue, NULL, 0);

	return eErrorCode;
}
